
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

package com.huawei.housekeeper.service.impl;

import com.huawei.housekeeper.common.constant.TaskConstant;
import com.huawei.housekeeper.common.enums.ErrorMsg;
import com.huawei.housekeeper.common.enums.StatusEnum;
import com.huawei.housekeeper.common.model.ServiceDetail;
import com.huawei.housekeeper.commonutils.constants.RabbitMqTopicConfig;
import com.huawei.housekeeper.commonutils.entity.OrderMessageEntity;
import com.huawei.housekeeper.commonutils.entity.TaskMessageEntity;
import com.huawei.housekeeper.commonutils.exception.Assert;
import com.huawei.housekeeper.commonutils.result.ListRes;
import com.huawei.housekeeper.commonutils.utils.CommonUtil;
import com.huawei.housekeeper.commonutils.utils.JsonUtil;
import com.huawei.housekeeper.commonutils.utils.MessageServiceUtil;
import com.huawei.housekeeper.config.CustomerContext;
import com.huawei.housekeeper.controller.convert.OrderConvert;
import com.huawei.housekeeper.controller.request.CreateOrderDto;
import com.huawei.housekeeper.controller.request.GetSkuDetailBySkuIdDto;
import com.huawei.housekeeper.controller.request.GetWorkUserInfoDto;
import com.huawei.housekeeper.controller.request.PageQueryOrderDto;
import com.huawei.housekeeper.controller.request.UpdateOrderDto;
import com.huawei.housekeeper.controller.response.GetOrderDetailsVo;
import com.huawei.housekeeper.controller.response.GetOrdersOfCustomerVo;
import com.huawei.housekeeper.controller.response.GetOrdersOfTenantVo;
import com.huawei.housekeeper.controller.response.GetWorkUserInfoVo;
import com.huawei.housekeeper.dao.entities.Order;
import com.huawei.housekeeper.dao.mapper.OrderMapper;
import com.huawei.housekeeper.service.OrderService;
import com.huawei.housekeeper.service.PublishService;
import com.huawei.housekeeper.service.UserinfoService;
import com.huawei.saashousekeeper.annotation.MqAround;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;

import lombok.extern.log4j.Log4j2;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

/**
 * 订单业务层
 *
 * @author lWX1128557
 * @since 2022-02-23
 */
@Service
@Log4j2
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    OrderMapper orderMapper;

    @Autowired
    PublishService publishService;

    @Autowired
    UserinfoService userinfoService;

    @Autowired
    MessageServiceUtil messageServiceUtil;

    /**
     * 用户查询订单列表
     *
     * @param pageQueryOrderDto 分页Dto
     * @return 用户订单列表Vo
     */
    @Override
    public ListRes<GetOrdersOfCustomerVo> getOrdersByCustomer(PageQueryOrderDto pageQueryOrderDto) {
        LambdaQueryWrapper<Order> select = Wrappers.lambdaQuery();
        Page<Order> page = new Page<>(pageQueryOrderDto.getCurrent(), pageQueryOrderDto.getSize());

        // 断言 如果得不到用户Id,则抛出异常
        Assert.notNull(CustomerContext.getCustomer().getUserId(), ErrorMsg.CUSTOMER_ERROR.getCode(),
            ErrorMsg.CUSTOMER_ERROR.getMessage());
        select.eq(Order::getCustomerId, CustomerContext.getCustomer().getUserId()).orderByDesc(Order::getCreatedTime);

        // 如果状态不为空则执行
        Optional<Integer> status = Optional.ofNullable(pageQueryOrderDto.getStatus());
        status.ifPresent(s -> select.eq(Order::getStatus, s));
        IPage<Order> orders = orderMapper.selectPage(page, select);
        List<GetOrdersOfCustomerVo> ordersOfCustomerVos =
            OrderConvert.INSTANCE.toGetOrdersOfCustomerVo(orders.getRecords());
        CustomerContext.removeCustomer();
        return new ListRes<>(ordersOfCustomerVos, (int) orders.getTotal());
    }

    /**
     * 租户查询订单列表
     *
     * @param pageQueryOrderDto 分页Dto
     * @return 租户订单列表Vo
     */
    @Override
    public ListRes<GetOrdersOfTenantVo> getOrdersByTenant(PageQueryOrderDto pageQueryOrderDto) {
        Page<Order> page = new Page<>(pageQueryOrderDto.getCurrent(), pageQueryOrderDto.getSize());
        LambdaQueryWrapper<Order> select = Wrappers.lambdaQuery();

        // 如果不为空则执行
        Optional<String> customerName = Optional.ofNullable(pageQueryOrderDto.getCustomerName());
        Optional<Integer> status = Optional.ofNullable(pageQueryOrderDto.getStatus());
        customerName.ifPresent(c -> select.eq(Order::getCustomerName, c));
        status.ifPresent(s -> select.eq(Order::getStatus, s));
        select.orderByDesc(Order::getCreatedTime);
        IPage<Order> orders = orderMapper.selectPage(page, select);
        List<GetOrdersOfTenantVo> ordersOfTenantVos = OrderConvert.INSTANCE.toGetOrdersOfTenantVo(orders.getRecords());
        return new ListRes<>(ordersOfTenantVos, (int) orders.getTotal());
    }

    /**
     * 根据订单编号查询订单详情
     *
     * @param orderNumber 订单号请求
     * @return 订单详情Vo
     */
    @Override
    public GetOrderDetailsVo getOrderByOrderNumber(String orderNumber) {
        LambdaQueryWrapper<Order> selectList = Wrappers.lambdaQuery();
        selectList.eq(Order::getOrderNumber, orderNumber);
        Order order = orderMapper.selectOne(selectList);
        Assert.notNull(order, ErrorMsg.SKUID_ERROR.getMessage());
        return OrderConvert.INSTANCE.mapOrderDetailsVo(order);
    }

    /**
     * 根据订单Id查询订单详情
     *
     * @param orderId 订单Id
     * @return 订单详情Vo
     */
    @Override
    public GetOrderDetailsVo getOrderByOrderId(Integer orderId) {
        LambdaQueryWrapper<Order> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Order::getId, orderId);
        Order order = orderMapper.selectOne(queryWrapper);

        // 防止在mapstruct映射order为空时导致空指针异常
        Assert.notNull(order, ErrorMsg.SKUID_ERROR.getMessage());
        return OrderConvert.INSTANCE.mapOrderDetailsVo(order);
    }

    /**
     * 添加订单
     *
     * @param createOrderDto 添加订单Dto
     * @return 下单结果
     * @throws JsonProcessingException json格式异常
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Long saveOrder(CreateOrderDto createOrderDto) throws JsonProcessingException {
        GetSkuDetailBySkuIdDto getSkuDetailBySkuIdDto = new GetSkuDetailBySkuIdDto();
        getSkuDetailBySkuIdDto.setSkuId(createOrderDto.getSkuId());

        // feign调用获取服务详情
        String detail = publishService.getSkuDetailBySkuId(getSkuDetailBySkuIdDto);
        ServiceDetail serviceDetail = JsonUtil.jsonToPojo(detail, ServiceDetail.class);

        Assert.notNull(serviceDetail.getServiceDesc(), ErrorMsg.SKUID_ERROR.getCode(),
            ErrorMsg.SKUID_ERROR.getMessage());
        Order order = new Order();
        order.setOrderNumber(System.currentTimeMillis() + CommonUtil.getUUID());
        order.setCustomerId(CustomerContext.getCustomer().getUserId());
        order.setCustomerName(createOrderDto.getCustomerName());
        order.setSkuId(createOrderDto.getSkuId());
        order.setPrice(serviceDetail.getServiceDesc().getPrice());
        order.setAppointmentTime(createOrderDto.getAppointmentTime());
        order.setCustomerPhone(createOrderDto.getCustomerPhone());
        order.setRemark(createOrderDto.getRemark());
        order.setAmount(createOrderDto.getAmount());
        order.setServiceDetail(JSONObject.toJSONString(serviceDetail));
        order
            .setPayment(serviceDetail.getServiceDesc().getPrice().multiply(new BigDecimal(createOrderDto.getAmount())));
        order.setAddress(createOrderDto.getAddress());
        order.setStatus(StatusEnum.WAITING.getStatus());
        orderMapper.insert(order);

        // 发送下单消息,队列：order.task
        OrderMessageEntity orderMessageEntity = OrderConvert.INSTANCE.mapOrderMessageEntity(order);
        messageServiceUtil.sendMsgToMsgService(RabbitMqTopicConfig.ROUTINGKEY_TASK_ORDER_NEWORDER, orderMessageEntity);

        // 移除用户变量
        CustomerContext.removeCustomer();
        return order.getId();
    }

    /**
     * 更新订单 队列task.order
     *
     * @param tenantDomain 租户标识
     * @param taskMessage 消息体
     */
    @Override
    @MqAround
    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = RabbitMqTopicConfig.QUEUE_ORDER),
        exchange = @Exchange(name = RabbitMqTopicConfig.EXCHANGE_TOPIC_AMQ, type = ExchangeTypes.TOPIC),
        key = {RabbitMqTopicConfig.ORDER_TASK_DO_TASK}))
    public void updateOrder(@Header String tenantDomain, @Payload String taskMessage) throws JsonProcessingException {
        Assert.notNull(taskMessage, ErrorMsg.MESSAGE_ERROR.getCode(), ErrorMsg.MESSAGE_ERROR.getMessage());
        TaskMessageEntity taskMessageEntity = JSONObject.parseObject(taskMessage, TaskMessageEntity.class);

        // 根据工人id查询工人信息
        GetWorkUserInfoDto getWorkUserInfoDto = new GetWorkUserInfoDto();
        getWorkUserInfoDto.setUserId(taskMessageEntity.getEmployeeId());
        String workInfo = userinfoService.getWorkInfo(getWorkUserInfoDto);
        GetWorkUserInfoVo workUserInfoVo = JsonUtil.jsonToPojo(workInfo, GetWorkUserInfoVo.class);

        // 查找属于哪个订单
        LambdaUpdateWrapper<Order> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(Order::getId, taskMessageEntity.getOrderId())
            .set(Order::getEmployeeId, taskMessageEntity.getEmployeeId())
            .set(Order::getEmployeeName, workUserInfoVo.getUserInfo().getUserName())
            .set(Order::getEmployeePhone, workUserInfoVo.getUserInfo().getPhoneNo())
            .set(Order::getUpdatedBy, workUserInfoVo.getUserInfo().getUserName());

        // 获取任务大厅发来的动作（接单、完成、取消）
        String action = taskMessageEntity.getAction();
        switch (action) {
            case TaskConstant.ACCEPT:
                updateWrapper.set(Order::getStatus, StatusEnum.ONGOING.getStatus());
                break;
            case TaskConstant.FINISHED:
                updateWrapper.set(Order::getStatus, StatusEnum.COMPLETED.getStatus());
                break;
            case TaskConstant.CANCEL:
                updateWrapper.set(Order::getStatus, StatusEnum.CANCEL.getStatus());
                break;
            default:
                break;
        }
        orderMapper.update(new Order(), updateWrapper);
    }

    /**
     * 用户修改订单状态
     *
     * @param updateOrderDto
     * @return
     */
    @Override
    public Integer updateOrder(UpdateOrderDto updateOrderDto) {
        LambdaUpdateWrapper<Order> update = Wrappers.lambdaUpdate();
        LambdaQueryWrapper<Order> query = Wrappers.lambdaQuery();
        query.select(Order::getStatus).eq(Order::getOrderNumber, updateOrderDto.getOrderNumber());
        Integer status = orderMapper.selectOne(query).getStatus();

        // 如果状态是取消或者已完成，则不能做修改
        Assert.isFalse(status.equals(StatusEnum.CANCEL.getStatus()) || status.equals(StatusEnum.COMPLETED.getStatus()),
            ErrorMsg.STATUS_ERROR.getCode(), ErrorMsg.STATUS_ERROR.getMessage());

        // 如果状态是待接单，且修改动作为取消订单，则可以完成。如果动作为完成订单，则不可以执行
        Assert.isFalse(
            status.equals(StatusEnum.WAITING.getStatus())
                && updateOrderDto.getStatus().equals(StatusEnum.COMPLETED.getStatus()),
            ErrorMsg.STATUS_ERROR.getCode(), ErrorMsg.STATUS_ERROR.getMessage());
        update.set(Order::getStatus, updateOrderDto.getStatus())
            .eq(Order::getOrderNumber, updateOrderDto.getOrderNumber());
        return orderMapper.update(new Order(), update);
    }
}
